package com.spzx.cart.service.impl;

import com.spzx.cart.api.domain.CartInfo;
import com.spzx.cart.service.ICartService;
import com.spzx.common.core.constant.SecurityConstants;
import com.spzx.common.core.context.SecurityContextHolder;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.api.domain.ProductSkuVo;
import com.spzx.product.api.domain.SkuPriceVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CartServiceImpl implements ICartService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RemoteProductService remoteProductService;

    private String getCartKey(Long userId) {
        //定义key user:userId:cart
        return "user:cart:" + userId;
    }

    @Override
    public void addToCart(Long skuId, Integer skuNum) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        //1.构建“用户”购物车hash结构key  user:cart:用户ID
        String cartKey = getCartKey(userId);

        //2.创建Hash结构绑定操作对象（方便对hash进行操作）
        BoundHashOperations<String,String,CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        //4.判断用户购物车中是否包含该商品 如果包含：数量进行累加(某件商品数量上限99) ：新增购物车商品
        String hashKey = skuId.toString();
        Integer threshold=99;
        if(hashOps.hasKey(hashKey)){
            //4.1 说明该商品在购物车中已有，对数量进行累加 ，不能超过指定上限99
            CartInfo cartInfo = hashOps.get(hashKey);
            int totalNum = cartInfo.getSkuNum()+skuNum;
            cartInfo.setSkuNum(totalNum > threshold ? threshold : totalNum);
            hashOps.put(hashKey,cartInfo);
        }else {
            //3.判断购物车商品种类（不同SKU）总数大于50件
            Long sizeCount = hashOps.size();
            if ( ++sizeCount > 50){
                throw  new ServiceException("商品种类数量超过上限");
            }
            //4. 说明购物车没有该商品，构建购物车对象，存入Redis
            CartInfo cartInfo = new CartInfo();
            cartInfo.setSkuNum(skuNum > threshold ? threshold : skuNum);
            cartInfo.setSkuId(skuId);
            //4.1 远程调用商品服务获取商品sku基本信息
            R<ProductSkuVo> productSkuResult = remoteProductService.getProductSku(skuId, SecurityConstants.INNER);
            if (R.FAIL == productSkuResult.getCode()) {
                throw new ServiceException(productSkuResult.getMsg());
            }
            ProductSkuVo productSkuVo = productSkuResult.getData();
            cartInfo.setSkuId(skuId);
            cartInfo.setSkuName(productSkuVo.getSkuName());
            cartInfo.setThumbImg(productSkuVo.getThumbImg());
            cartInfo.setCartPrice(productSkuVo.getSalePrice());
            cartInfo.setSkuPrice(productSkuVo.getSalePrice());
            cartInfo.setCreateTime(new Date());
            //4.2 将购物车商品存入Redis
            hashOps.put(hashKey,cartInfo);
        }
    }

    @Override
    public List<CartInfo> getCartList() {
        //当前用户id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = this.getCartKey(userId);
        //从redis获取购物车数据
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cartKey);
        if(!CollectionUtils.isEmpty(cartInfoList)){
            //非空降序排序后返回
            List<CartInfo> infoList = cartInfoList.stream()
                    .sorted((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()))
                    .collect(Collectors.toList());
            return infoList;
        }
        //空直接返回空数据
        return new ArrayList<>();
    }

    @Override
    public void deleteCart(Long skuId) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = this.getCartKey(userId);
        // 获取缓存对象并根据skuId删除对应数据
        redisTemplate.boundHashOps(cartKey).delete(skuId.toString());
    }

    @Override
    public void checkCart(Long skuId, Integer isChecked) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        // 获取缓存
        String cartKey = this.getCartKey(userId);
        BoundHashOperations<String,String,CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        // 判断缓存购物车是否有该商品
        if(hashOps.hasKey(cartKey)){
            CartInfo cartInfo = hashOps.get(cartKey);
            // cartInfoUpd 写回缓存
            cartInfo.setIsChecked(isChecked);
            hashOps.put(skuId.toString(),cartInfo);
        }
    }

    @Override
    public void allCheckCart(Integer isChecked) {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = this.getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = hashOps.values();
        cartInfoList.forEach(item->{
            CartInfo cartInfo = hashOps.get(item.getSkuId().toString());
            cartInfo.setIsChecked(isChecked);
            hashOps.put(item.getSkuId().toString(),cartInfo);

            //如果从redis获取缓存还未到修改缓存这段时间内数据被修改（遍历操作耗时相对较长），就会造成返货错误结果
            //因此在遍历后应再次获取对象，保证对象的数据是最新的，再进行修改。
            /*item.setIsChecked(isChecked);
            hashOps.put(item.getSkuId().toString(),item);*/ //可能会发生返回结果错误
        });
    }

    @Override
    public void clearCart() {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        //获取缓存对象。删除对应标识的对象缓存
        redisTemplate.delete(cartKey);
    }

    @Override
    public List<CartInfo> getCartCheckedList() {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = this.getCartKey(userId);
        List<CartInfo> cartCachInfoList = redisTemplate.opsForHash().values(cartKey);
        List<CartInfo> cartInfos = new ArrayList<>();
        if (!CollectionUtils.isEmpty(cartCachInfoList)) {
            cartInfos = cartCachInfoList
                    .stream()
                    .filter(item -> item.getIsChecked().intValue() == 1)
                    .collect(Collectors.toList());
        }
        return cartInfos;
    }

    @Override
    public Boolean updateCartPrice() {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);

        //获取购物车中的商品列表
        List<CartInfo> cartInfoList = hashOps.values();
        cartInfoList.stream().forEach(cartInfo -> {
            // 获取实际商品价格
            R<SkuPriceVo> skuPriceVoResult = remoteProductService.getSkuPrice(cartInfo.getSkuId(), SecurityConstants.INNER);
            if(R.FAIL == skuPriceVoResult.getCode()){
                throw new ServiceException(skuPriceVoResult.getMsg());
            }
            SkuPriceVo skuPriceVo = skuPriceVoResult.getData();
            //cartInfo.setCartPrice(skuPriceVo.getSalePrice());//放入购物车时的商品价格
            if(skuPriceVo.getSalePrice().compareTo(cartInfo.getSkuPrice()) != 0){
                cartInfo.setSkuPrice(skuPriceVo.getSalePrice());//实时价格
                hashOps.put(cartInfo.getSkuId().toString(), cartInfo);
            }
        });

        return true;
    }

    @Override
    public Boolean deleteCartCheckedList() {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = this.getCartKey(userId);
        BoundHashOperations<String,String,CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoListResult = hashOps.values();
        if(!CollectionUtils.isEmpty(cartInfoListResult)){
            for (CartInfo cartInfo : cartInfoListResult) {
                // 删除选中的商品
                if (cartInfo.getIsChecked().intValue() == 1) {
                    hashOps.delete(cartInfo.getSkuId().toString());
                }
            }
        }
        return true;
    }
}